////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxncsintfqsee.cpp
/// @brief CamX NCS Interface QSEE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// NOWHINE FILE CP006: QSEE Sensor interface requires us to use std string class
// NOWHINE FILE CP010: QSEE Sensor interface
// NOWHINE FILE GR004: Need to get the const variables within this scope

#include "camxncsintfqsee.h"
#include "camxncssensor.h"
#include "camxncssensordata.h"
#include "camxchicontext.h"
#include "camxncsservice.h"

CAMX_NAMESPACE_BEGIN

using namespace std;

#define NCS_DEBUG_DUMP 0

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::Create(
    NCSIntfQSEE** ppNCSObject,
    ChiContext*   pChiContext,
    NCSService*   pServiceObject)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(NULL != ppNCSObject);

    *ppNCSObject = CAMX_NEW NCSIntfQSEE();
    if (NULL != *ppNCSObject)
    {
        result = (*ppNCSObject)->Initialize(pChiContext);
        if (result != CamxResultSuccess)
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to init the QSEE object");
            CAMX_DELETE (*ppNCSObject);
            *ppNCSObject = NULL;
        }
        else
        {
            (*ppNCSObject)->m_pServiceObject = pServiceObject;
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to create new object, no memory !!");
        result       = CamxResultENoMemory;
        *ppNCSObject = NULL;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::NCSIntfQSEE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSIntfQSEE::NCSIntfQSEE()
{

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::~NCSIntfQSEE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSIntfQSEE::~NCSIntfQSEE()
{
    NCSSensorData* pSensorData = NULL;
    LDLLNode*      pNode       = NULL;

    if (NULL != m_pQSEEIntfMutex)
    {
        m_pQSEEIntfMutex->Destroy();
        m_pQSEEIntfMutex = NULL;
    }

    if (NULL != m_pQSEELinkUpdateMutex)
    {
        m_pQSEELinkUpdateMutex->Destroy();
        m_pQSEELinkUpdateMutex = NULL;
    }

    if (NULL != m_pQSEELinkUpdateCond)
    {
        m_pQSEELinkUpdateCond->Destroy();
        m_pQSEELinkUpdateCond = NULL;
    }

    // Destroy sensor data objects
    pNode = m_sensorDataObjectList.RemoveFromHead();

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Destryoing NCS data objects!!");
    while (NULL != pNode)
    {
        pSensorData = static_cast<NCSSensorData*>(pNode->pData);
        if (NULL != pSensorData)
        {
            CAMX_DELETE pSensorData;
            pSensorData = NULL;
        }
        CAMX_FREE(pNode);

        pNode = m_sensorDataObjectList.RemoveFromHead();
    }

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "QSEE handle destroyed");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// INCSIntfBase::~INCSIntfBase
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
INCSIntfBase::~INCSIntfBase()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetListOfSensors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::GetListOfSensors()
{
    CamxResult result        = CamxResultSuccess;

    // Set the number of sensors to 0.
    m_numSensors = 0;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Getting list of sensors");

    /// Probe for the sensors one at a time.
    for (INT sensorType = 0; sensorType < NCSMaxType; sensorType++)
    {
        // 验证该传感器是否可用？
        result = ProbeSensor(static_cast<NCSSensorType>(sensorType));
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Failed probe for sensor %d", sensorType);
            continue;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::QueryCapabilites
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::QueryCapabilites()
{
    CamxResult      result      = CamxResultSuccess;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Querying for sensor capabilities");

    CAMX_ASSERT(NULL != m_pQSEELinkUpdateMutex);

    for (INT sensorType = 0; sensorType < NCSMaxType; sensorType++)
    {
        // if sensor is probed then query caps, else don't.
        if (0 != (m_sensorList & (1 << sensorType)))
        {
            result = RequestSensorCaps(static_cast<NCSSensorType>(sensorType));
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Failed getting capabilities of sensor %d", sensorType);
                continue;
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Sensor %d not probed", sensorType);
            continue;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SendAttribRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID NCSIntfQSEE::SendAttribRequest(
    INT sensorType)
{
    ssc_connection*        phSSCHandle = NULL;
    sensor_uid*            pSUID       = NULL;
    sns_client_request_msg pb_req_msg;
    sns_std_attr_req       pb_attr_req;
    string                 pb_attr_req_encoded;
    string                 pb_req_msg_encoded;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Requesting for attributes for sensor %d", sensorType);

    m_sensorCaps[sensorType].isValid = FALSE;

    pSUID       = &m_suids[sensorType];

    phSSCHandle = m_pProbeLink;

    pb_attr_req.set_register_updates(false);
    pb_attr_req.SerializeToString(&pb_attr_req_encoded);
    pb_req_msg.set_msg_id(SNS_STD_MSGID_SNS_STD_ATTR_REQ);

    pb_req_msg.mutable_request()->set_payload(pb_attr_req_encoded);
    pb_req_msg.mutable_suid()->set_suid_high(pSUID->high);
    pb_req_msg.mutable_suid()->set_suid_low(pSUID->low);
    pb_req_msg.mutable_susp_config()->set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP);
    pb_req_msg.mutable_susp_config()->set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS);
    pb_req_msg.SerializeToString(&pb_req_msg_encoded);

    phSSCHandle->send_request(pb_req_msg_encoded);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SendCalibRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::SendCalibRequest(
    INT            sensorType,
    ssc_connection* phSSCHandle)
{
    sensor_uid*            pSUID = NULL;
    sns_client_request_msg pb_req_msg;
    string                 pb_req_msg_encoded;
    CamxResult             result = CamxResultSuccess;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Requesting for calibration data for sensor %d", sensorType);

    switch (sensorType)
    {
        case NCSGyroType:
            sensorType = NCSGyroCalType;
            break;
        case NCSMagnetometerType:
            sensorType = NCSMagCalType;
            break;
        default:
            sensorType = NCSMaxType;
            break;
    }

    pSUID = &m_suids[sensorType];

    CAMX_ASSERT_MESSAGE((NULL != phSSCHandle), "invalid ssc connection handle");
    if (NULL != phSSCHandle)
    {
        pb_req_msg.set_msg_id(SNS_STD_SENSOR_MSGID_SNS_STD_ON_CHANGE_CONFIG);
        pb_req_msg.mutable_request()->clear_payload();

        pb_req_msg.mutable_suid()->set_suid_high(pSUID->high);
        pb_req_msg.mutable_suid()->set_suid_low(pSUID->low);

        pb_req_msg.mutable_request()->mutable_batching()->set_batch_period(0);
        pb_req_msg.mutable_request()->mutable_batching()->set_flush_period(UINT32_MAX);

        // @todo (CAMX-2393) check if need for delivery wakeup , refer sensor code.
        pb_req_msg.mutable_susp_config()->set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP);
        pb_req_msg.mutable_susp_config()->set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS);
        pb_req_msg.SerializeToString(&pb_req_msg_encoded);

        phSSCHandle->send_request(pb_req_msg_encoded);
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_LOG_ERROR(CamxLogGroupNCS, "invalid ssc connection handle");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SendConfigRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::SendConfigRequest(
    INT connIndex,
    NCSSensorConfig*  pSensorConfig)
{
    string                 pb_req_msg_encoded;
    string                 config_encoded;
    sns_client_request_msg pb_req_msg;
    sns_std_sensor_config  config;
    sensor_uid*            pSUID = NULL;
    ssc_connection*        pConn = NULL;
    NCSSensorType          sensorType;
    CamxResult             result = CamxResultSuccess;

    sensorType = m_sensorConnList[connIndex].sensorType;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Sending config request for connIndex %d sensor type %d",
                     connIndex,
                     sensorType);

    pSUID = &m_suids[sensorType];
    pConn = m_sensorConnList[connIndex].phSensorConnHandle;

    if ((pConn != NULL) && (pSUID != NULL) && (pSensorConfig != NULL))
    {
        m_sensorConnList[connIndex].suid = *pSUID;

        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "SampleRate %f ReportRate %d sensor type %d suidH 0x%x suidL 0x%x",
                         pSensorConfig->samplingRate,
                         pSensorConfig->reportRate,
                         pSensorConfig->sensorType,
                         pSUID->high,
                         pSUID->low);

        config.set_sample_rate(pSensorConfig->samplingRate);
        config.SerializeToString(&config_encoded);

        pb_req_msg.mutable_request()->mutable_batching()->set_batch_period(pSensorConfig->reportRate);
        pb_req_msg.set_msg_id(SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG);
        pb_req_msg.mutable_request()->set_payload(config_encoded);
        pb_req_msg.mutable_suid()->set_suid_high(pSUID->high);
        pb_req_msg.mutable_suid()->set_suid_low(pSUID->low);
        pb_req_msg.mutable_susp_config()->set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP);
        pb_req_msg.mutable_susp_config()->set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS);

        pb_req_msg.SerializeToString(&pb_req_msg_encoded);
        pConn->send_request(pb_req_msg_encoded);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to Send config, invalid params conn %p suid %p sensorConfig %p",
                       pConn,
                       pSUID,
                       pSensorConfig);
        result = CamxResultEInvalidPointer;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SendDisableRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::SendDisableRequest(
    INT connIndex)
{
    sns_client_request_msg pb_req_msg;
    sensor_uid*            pSUID       = NULL ;
    ssc_connection*        phSSCHandle = NULL;
    string                 pb_req_msg_encoded;
    NCSSensorType          sensorType;
    CamxResult             result      = CamxResultSuccess;

    if (connIndex >= 0)
    {
        sensorType  = m_sensorConnList[connIndex].sensorType;
        pSUID       = &m_sensorConnList[sensorType].suid;
        phSSCHandle = m_sensorConnList[connIndex].phSensorConnHandle;

        if ((NULL != pSUID) && (NULL != phSSCHandle))
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Sending disable request for connIndex %d sensor type %d",
                connIndex,
                sensorType);

            pb_req_msg.set_msg_id(SNS_CLIENT_MSGID_SNS_CLIENT_DISABLE_REQ);
            pb_req_msg.mutable_suid()->set_suid_high(pSUID->high);
            pb_req_msg.mutable_suid()->set_suid_low(pSUID->low);
            pb_req_msg.mutable_susp_config()->set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP);
            pb_req_msg.mutable_susp_config()->set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS);

            pb_req_msg.SerializeToString(&pb_req_msg_encoded);
            phSSCHandle->send_request(pb_req_msg_encoded);
        }
        else
        {
            result = CamxResultEInvalidArg;
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Sending disable request failed");
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Sending disable request failed");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::ProbeSensor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::ProbeSensor(
    NCSSensorType sensorType)
{
    BOOL       isSensorValid = FALSE;
    CamxResult result        = CamxResultSuccess;

    /// This is a lamda function needed to be passed on to the Sensors API.
    /// The 'this' param is passed on so that it is populated with the data.
    suid_lookup* pLookup = CAMX_NEW suid_lookup(
        [this](const string& datatype, const vector<sensor_uid>& suids)
    {
        CAMX_LOG_INFO(CamxLogGroupNCS, "Received SUID event with length %zu datatype %s", suids.size(), datatype.c_str());
        if (suids.size() > 0)
        {
            sensor_uid suid = suids.at(0);
            SetupSensorLinkOnly(&suid, this, datatype);
        }
    });

    if (NULL != pLookup)
    {
        switch (sensorType)
        {
            case NCSGyroType:
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Requesting GYRO probe, Waiting max for a 1 sec");
                pLookup->request_suid("gyro");
                isSensorValid = TRUE;
                break;
            case NCSAccelerometerType:
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Requesting ACCEL probe, Waiting max for a 1 sec");
                pLookup->request_suid("accel");
                isSensorValid = TRUE;
                break;
            case NCSGravityType:
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Request Gravity probe, Waiting max for a 1 sec");
                pLookup->request_suid("gravity");
                isSensorValid = TRUE;
                break;
            case NCSGyroCalType:
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Requesting GYRO Calibration probe, Waiting max for a 1 sec");
                pLookup->request_suid("gyro_cal");
                isSensorValid = TRUE;
                break;
            case NCSMagCalType:
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Requesting MAG Calibration probe, Waiting max for a 1 sec");
                pLookup->request_suid("mag_cal");
                isSensorValid = TRUE;
                break;
            default:
                break;
        }

        if (TRUE == isSensorValid)
        {
            /// Wait for async callback.
            m_pQSEELinkUpdateMutex->Lock();

            while (0 == (m_sensorList & (1 << sensorType)))
            {
                result = m_pQSEELinkUpdateCond->TimedWait(
                    m_pQSEELinkUpdateMutex->GetNativeHandle(),
                    QSEEConnectionTimeout);
                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Timed wait failed with result %d while probing sensor %d !!",
                                   result,
                                   sensorType);
                    break;
                }
                else
                {
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Probe success for sensor %d!!",
                                     sensorType);
                }
            }

            m_pQSEELinkUpdateMutex->Unlock();
        }
        else
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Sensor unsupported %d !!",
                             sensorType);
            result = CamxResultSuccess;
        }

        CAMX_DELETE pLookup;
        pLookup = NULL;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to create lookup object, out of memory !!");
        result = CamxResultENoMemory;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::RequestSensorCaps
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::RequestSensorCaps(
    NCSSensorType sensorType)
{
    CamxResult      result      = CamxResultSuccess;
    ssc_connection* phSSCHandle = NULL;

    phSSCHandle = m_pProbeLink;

    // If the source is a non-calibration source, only then query attributes.
    // Calibration sources don't have attributes to probe for.
    if ((NULL != phSSCHandle) && (FALSE == isCalibSource(sensorType)))
    {
        SendAttribRequest(sensorType);

        m_pQSEELinkUpdateMutex->Lock();

        while (TRUE != m_sensorCaps[sensorType].isValid)
        {
            result = m_pQSEELinkUpdateCond->TimedWait(
                m_pQSEELinkUpdateMutex->GetNativeHandle(),
                QSEEConnectionTimeout);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Fill attribute timed wait failed with result %d for sensor %d",
                               result,
                               sensorType);
                break;
            }
            else
            {
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Attribute query success sensor %d", sensorType);
                break;
            }
        }

        m_pQSEELinkUpdateMutex->Unlock();

        if (CamxResultSuccess == result)
        {
            if (0 != (m_sensorList & (1 << sensorType)))
            {
                m_sensorCaps[sensorType].type = static_cast<NCSSensorType>(sensorType);
            }
            else
            {
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Unsupported sensor");
                result = CamxResultEUnsupported;
            }
        }
        else
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Timeout while querying for capabilities !!");
            result = CamxResultETimeout;
        }

    }
    else
    {
        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Sensor not available");
        result = CamxResultSuccess;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SetBufferLock
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL NCSIntfQSEE::SetBufferLock(
    INT connIndex,
    INT start,
    INT end)
{
    INT  curPos;
    BOOL result = FALSE;
    // current position is the location where the sensor would write to in the next callback
    curPos = m_sensorConnList[connIndex].bufferHandles.currentPos;

    if (end >= start)
    {
        // Positions: 0...start...curPos...end...(N-1)
        if ((curPos <= end) && (curPos >= start))
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "bufLocked start %d end %d curPos %d", start, end, curPos);
            m_sensorConnList[connIndex].bufferHandles.curPosLocked += 1;
            result =  TRUE;
        }
    }
    // If the start and end positions warp around the ring buffer
    else
    {
        // Positions: 0...curPos..end ...start....(N - 1)
        // Positions: 0...end.....start..curPos...(N - 1)
        if ((curPos <= end) || (curPos >= start))
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "bufLocked start %d end %d curPos %d", start, end, curPos);
            m_sensorConnList[connIndex].bufferHandles.curPosLocked += 1;
            result = TRUE;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::ClearBufferLock
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::ClearBufferLock(
    INT connIndex)
{
    CamxResult result = CamxResultSuccess;

    if (0 != m_sensorConnList[connIndex].bufferHandles.curPosLocked)
    {
        m_sensorConnList[connIndex].bufferHandles.curPosLocked -= 1;
    }
    else
    {
        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Unable to clear buffer Q lock, no locks on this yet");
        result = CamxResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::RateMatch
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL NCSIntfQSEE::RateMatch(
    NCSSensorConfig* pSensorConfig,
    INT              connIndex,
    BOOL*            pNeedReconfig)
{
    QSEESensorConn* pSensorConn = NULL;
    BOOL            isMatched   = FALSE;

    CAMX_ASSERT((NULL != pSensorConfig) && (0 <= connIndex));

    if (FALSE == pSensorConfig->needExact)
    {
        pSensorConn = &m_sensorConnList[connIndex];

        if ((NULL != pSensorConn->phSensorConnHandle) &&
            (pSensorConn->sensorType == pSensorConfig->sensorType))
        {
            isMatched      = TRUE;
            if (pSensorConfig->samplingRate > pSensorConn->curSamplingRate)
            {
                *pNeedReconfig = TRUE;
            }
        }
    }
    else
    {
        isMatched = FALSE;
    }

    return isMatched;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::CreateClientSession
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::CreateClientSession(
    NCSSensorHandle hSensor)
{
    UINT            numActiveClients  = 0;
    NCSSensor*      phNCSSensorHandle = NULL;
    CamxResult      result            = CamxResultSuccess;
    NCSSensorType   sensorType        = NCSMaxType;
    NCSSensorType   calibSensorType   = NCSMaxType;
    NCSSensorConfig sensorConfig      = { 0 };
    NCSSensorData*  pSensorData       = NULL;
    NCSSensorType   calSensorType     = NCSMaxType;
    NCSSensor*      pSensorObject     = NULL;
    BOOL            isMatched         = FALSE;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Creating SNS client session");

    CAMX_ASSERT(NULL != hSensor);

    pSensorObject = static_cast<NCSSensor*>(hSensor);

    if (NULL != pSensorObject)
    {

        pSensorObject->FillSensorConfig(&sensorConfig);

        // Is the sensor is probed and valid
        if (0 != (m_sensorList & (1 << sensorConfig.sensorType)))
        {
            sensorType = sensorConfig.sensorType;

            for (UINT connIndex = 0; connIndex < NCSMaxSupportedConns; connIndex++)
            {
                /// If sensor connection handle is not NULL
                if (NULL != m_sensorConnList[connIndex].phSensorConnHandle)
                {
                    /// If sensor type matches,check if there is a scope for rate match
                    if (m_sensorConnList[connIndex].sensorType == sensorType)
                    {
                        /// If the rates don't match and this current client cannot be mapped to
                        /// existing connection, create a new link and other necessary objects.
                        BOOL isReconfigNeeded = FALSE;

                        isMatched = RateMatch(&sensorConfig, connIndex, &isReconfigNeeded);
                        if (FALSE == isMatched)
                        {
                            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "No match at connIndex %d sensor type %d continuing...",
                                             connIndex, sensorConfig.sensorType);
                            continue;
                        }
                        else
                        {
                            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Rate match at connIndex %d sensor type %d",
                                             connIndex, sensorConfig.sensorType);

                            if (TRUE == isReconfigNeeded)
                            {
                                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Need reconfig, doing it");

                                result = ReconfigSession(&sensorConfig, connIndex, FALSE);
                                if (CamxResultSuccess != result)
                                {
                                    CAMX_LOG_ERROR(CamxLogGroupNCS,
                                                     "Reconfig failed connIndex %d sensor type %d trying to reconfig %s",
                                                     connIndex, sensorConfig.sensorType,
                                                     CamxResultStrings[result]);
                                    break;
                                }
                                else
                                {
                                    CAMX_LOG_VERBOSE(CamxLogGroupNCS,
                                                   "Reconfig success connIndex %d sensor type %d",
                                                   connIndex,
                                                   sensorConfig.sensorType);
                                    pSensorObject->SetConnIndex(connIndex);
                                    break;
                                }
                            }
                            // No need for reconfig, same conn can be reused
                            else
                            {
                                pSensorObject->SetConnIndex(connIndex);
                                m_sensorConnList[connIndex].mappedClients++;
                                break;
                            }

                        }
                    }
                    else
                    {
                        // If sensor type does not match at index of a valid element in connection list, continue.
                        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "sensor type does not match at index %d continue", connIndex);
                        continue;
                    }
                }
                /// Else create new connection at the current connection index and break
                else
                {
                    result = CreateQSEEConnHandle(connIndex, &sensorConfig);
                    if (CamxResultSuccess == result)
                    {
                        pSensorObject->SetConnIndex(connIndex);
                        m_sensorConnList[connIndex].mappedClients++;
                        break;
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to create a QSEE connection");
                        break;
                    }
                }
            }

            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Mapped client at index %d sensorType %d mapped clients %d",
                             pSensorObject->GetConnIndex(),
                             pSensorObject->GetSensorType(),
                             m_sensorConnList[pSensorObject->GetConnIndex()].mappedClients);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to configure all sensors, sensor not probed !!");
            result = CamxResultEUnsupported;
        }
    }
    else
    {
        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "No clients registered !!");
        result = CamxResultSuccess;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::CreateQSEEConnHandle
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::CreateQSEEConnHandle(
    INT              connIndex,
    NCSSensorConfig* pSensorConfig)
{
    CamxResult      result          = CamxResultSuccess;
    NCSSensorType   sensorType      = NCSMaxType;
    NCSSensorType   calibSensorType = NCSMaxType;
    size_t          bufferSize      = 0;
    UINT            bufferStride    = 0;
    sensor_uid*     pSensorSUID     = NULL;
    ssc_connection* phSSCHandle     = NULL;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "NCS: connIdx %d sampling rate %f report rate %d",
                     connIndex, pSensorConfig->samplingRate, pSensorConfig->reportRate);

    sensorType = pSensorConfig->sensorType;

    if (FALSE == isValidSensorType(sensorType))
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Invalid sensor type: %d", sensorType);
        return CamxResultEInvalidArg;
    }

    m_sensorConnList[connIndex].pRequestMutex   = Mutex::Create("AsyncRequestMutex");
    m_sensorConnList[connIndex].pRequestCondVar = Condition::Create("AsyncRequestCondvar");

    // Allocate buffer only if the sensor sampling rate is valid
    if (pSensorConfig->samplingRate > 0.0f)
    {
        m_sensorConnList[connIndex].bufferHandles.phBufferHandle =
            AllocSensorStreamBuf(*pSensorConfig, &bufferSize, &bufferStride);

#if NCS_DEBUG_DUMP
        m_bufferHandles[connIndex].pFileBuffer =
            OsUtils::FOpen(NCSLogPath, "w+");

        if (NULL == m_bufferHandles[connIndex].pFileBuffer)
        {
            CAMX_LOG_WARN(CamxLogGroupNCS, "Unable to open dump file !!");
        }
#endif // NCS_DEBUG_DUMP

        if (NULL != m_sensorConnList[connIndex].bufferHandles.phBufferHandle)
        {
            m_sensorConnList[connIndex].bufferHandles.bufferSize = bufferSize;
            m_sensorConnList[connIndex].bufferHandles.currentPos = 0;
            m_sensorConnList[connIndex].bufferHandles.bufferStride = bufferStride;
            m_sensorConnList[connIndex].bufferHandles.pBufferQMutex =
                Mutex::Create("BufferQMutex");
            m_sensorConnList[connIndex].bufferHandles.curPosLocked = 0;
            m_sensorConnList[connIndex].bufferHandles.isBufferFull = FALSE;
            // Tick period, for a given sampling rate.
            m_sensorConnList[connIndex].bufferHandles.tickPeriod =
                static_cast<INT>((1 / pSensorConfig->samplingRate) * static_cast<FLOAT>(NCSQtimerFrequency));

            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Number of mapped clients at connIndex %d mappedClients %d",
                           connIndex,
                           m_sensorConnList[connIndex].mappedClients);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Create ring buffer failed");
            result = CamxResultENoMemory;
        }

    }
    else
    {
        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Invalid sampling rate %f, No initialization done for this sensor %d",
                         pSensorConfig->samplingRate, connIndex);
        result = CamxResultEInvalidArg;
    }

    if (CamxResultSuccess == result)
    {
        /// Create ssc connection for a client.
        /// This is a lamda function needed to be passed on to the Sensors API.
        /// The 'this' param is passed on so that it is populated with the data.
        phSSCHandle = CAMX_NEW ssc_connection(
            [this](const uint8_t* pData, size_t size)
        {
            this->SensorCallback(pData, size);
        });

        m_sensorConnList[connIndex].phSensorConnHandle = phSSCHandle;
        m_sensorConnList[connIndex].sensorType = sensorType;

        /// Use the suid from the suid list of the sensors probed during bootup.
        /// Copy the same into the connection list.
        pSensorSUID = &m_suids[sensorType];
        if (NULL != pSensorSUID)
        {
            m_sensorConnList[connIndex].suid = *pSensorSUID;
        }

        SendConfigRequest(connIndex, pSensorConfig);
        m_sensorConnList[connIndex].connectionState = QSEEConnRunning;
        // Initialize last seen timestamp to the point when we send config request to sensor.
        OsUtils::GetTime(&m_sensorConnList[connIndex].lastSeenTime);

        m_sensorConnList[connIndex].curSamplingRate = pSensorConfig->samplingRate;
        m_sensorConnList[connIndex].curReportRate = pSensorConfig->reportRate;

        // Create corresponding calibration source links and configure the same over the connection created earlier
        if (TRUE == isCalibNeeded(sensorType))
        {
            SendCalibRequest(sensorType, phSSCHandle);
            switch (sensorType)
            {
                case NCSGyroType:
                    calibSensorType = NCSGyroCalType;
                    break;
                case NCSMagnetometerType:
                    calibSensorType = NCSMagCalType;
                    break;
                default:
                    calibSensorType = NCSMaxType;
                    break;
            }
            if (TRUE == isValidSensorType(calibSensorType))
            {
                m_sensorConnList[connIndex].calibSUID =
                    m_suids[calibSensorType];
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Invalid sensor type");
            }
        }

        m_sensorConnList[connIndex].isValid = TRUE;

        m_numActiveConns++;

        CAMX_LOG_VERBOSE(CamxLogGroupNCS,
                         "Conn Idx %d sensorType %d buffHand %p size %d sscConn %p tick %d numConns %d suidH 0x%x",
                         connIndex,
                         sensorType,
                         m_sensorConnList[connIndex].bufferHandles.phBufferHandle,
                         bufferSize,
                         m_sensorConnList[connIndex].phSensorConnHandle,
                         m_sensorConnList[connIndex].bufferHandles.tickPeriod,
                         m_numActiveConns,
                         m_sensorConnList[connIndex].suid.high);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetDataAsync
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::GetDataAsync(
    UINT64        tStart,
    UINT64        tEnd,
    INT           connIndex,
    VOID*         pFence)
{
    INT              currentPos           = 0;
    INT              startLocation        = 0;
    UINT             bufferSize           = 0;
    UINT             bufferStride         = 0;
    UINT8*           phSensorBufferHandle = NULL;
    INT              bufferQLength        = 0;
    UINT64           tCurrent             = 0;
    NCSAsyncRequest* pAsyncRequest        = NULL;
    BOOL             isSensorValid        = TRUE;
    NCSSensorType    sensorType           = NCSMaxType;
    CamxResult       result               = CamxResultSuccess;

    CAMX_ASSERT((NULL != pFence) && (tStart <= tEnd) && ((connIndex >= 0) &&
        (connIndex < static_cast<INT>(NCSMaxSupportedConns))));

    sensorType = m_sensorConnList[connIndex].sensorType;

    // Intialize buffer parameters
    // lock buffer
    m_sensorConnList[connIndex].pRequestMutex->Lock();

    phSensorBufferHandle = static_cast<UINT8*>(m_sensorConnList[connIndex].bufferHandles.phBufferHandle);
    bufferSize           = static_cast<UINT>(m_sensorConnList[connIndex].bufferHandles.bufferSize);
    bufferStride         = m_sensorConnList[connIndex].bufferHandles.bufferStride;
    currentPos           = m_sensorConnList[connIndex].bufferHandles.currentPos;

    CAMX_ASSERT((NULL != phSensorBufferHandle) && (0 != bufferSize) && (0 != bufferStride));

    bufferQLength = bufferSize / bufferStride;

    // currentPos is the location where the next data would be added.
    startLocation = currentPos - 1;

    switch (sensorType)
    {
        case NCSGyroType:
            tCurrent = reinterpret_cast<NCSDataGyro*>(phSensorBufferHandle + startLocation*bufferStride)->timestamp;
            break;
        case NCSAccelerometerType:
            tCurrent = reinterpret_cast<NCSDataAccel*>(phSensorBufferHandle + startLocation*bufferStride)->timestamp;
            break;
        case NCSGravityType:
            tCurrent = reinterpret_cast<NCSDataGravity*>(phSensorBufferHandle + startLocation*bufferStride)->timestamp;
            break;
        default:
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unsupported sensor type");
            isSensorValid =  FALSE;
            break;
    }

    if ((TRUE == isSensorValid) &&
        (QSEEConnRunning == m_sensorConnList[connIndex].connectionState))
    {

        // Alloc the request structure
        pAsyncRequest = static_cast<NCSAsyncRequest*>(CAMX_CALLOC(sizeof(NCSAsyncRequest)));
        if (NULL != pAsyncRequest)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "tStart %llu tEnd %llu phFence %p",
                           tStart, tEnd, pAsyncRequest->hChiFence);

            pAsyncRequest->tEnd    = tEnd;
            pAsyncRequest->tStart  = tStart;
            pAsyncRequest->hChiFence = (static_cast<CHIFENCEHANDLE>(pFence));

            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Pending Async requests %d connIndex %d",
                             m_sensorConnList[connIndex].asyncRequestQ.NumNodes(), connIndex);

            LDLLNode* pNode = static_cast<LDLLNode*>(CAMX_CALLOC(sizeof(LDLLNode)));

            if (NULL != pNode)
            {
                pNode->pData = pAsyncRequest;
                m_sensorConnList[connIndex].asyncRequestQ.InsertToTail(pNode);
            }
            else
            {
                result = CamxResultENoMemory;
            }

        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to allocate async request object");
            result = CamxResultENoMore;
        }

    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Invalid sensor or connection in invalid state: %d",
                       m_sensorConnList[connIndex].connectionState);
        result = CamxResultEInvalidState;
    }

    m_sensorConnList[connIndex].pRequestMutex->Unlock();
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::TriggerClientFence
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::TriggerClientFence(
    QSEEJob* pJob)
{
    UINT64                       tStart          = 0;
    UINT64                       tEnd            = 0;
    UINT64                       tCurrent        = 0;
    CHIFENCEHANDLE               hFence          = NULL;
    NCSAsyncRequest*             phRequestHandle = NULL;
    LightweightDoublyLinkedList* pClientRequestQ = NULL;
    CamxResult                   result          = CamxResultSuccess;
    INT                          connIndex       = NCSMaxSupportedConns;

    CAMX_ASSERT(NULL != pJob);

    connIndex = pJob->connIndex;

    CAMX_ASSERT_MESSAGE((connIndex >= 0) && (connIndex < static_cast<INT>(NCSMaxSupportedConns)),
                "connIndex %d", connIndex);

    // Only process if connection has been properly initialized
    if (NULL != m_sensorConnList[connIndex].pRequestMutex)
    {
        pClientRequestQ = &m_sensorConnList[connIndex].asyncRequestQ;

        m_sensorConnList[connIndex].pRequestMutex->Lock();

        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Pending async request %d connIndex %d",
                            pClientRequestQ->NumNodes(), connIndex);

        LDLLNode* pNode = pClientRequestQ->Head();
        LDLLNode* pNext = LightweightDoublyLinkedList::NextNode(pNode);

        for (; NULL != pNode; pNode = pNext)
        {
            pNext = LightweightDoublyLinkedList::NextNode(pNode);

            phRequestHandle = static_cast<NCSAsyncRequest*>(pNode->pData);
            if (NULL == phRequestHandle)
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "pRequestHandle NULL");

                pClientRequestQ->RemoveNode(pNode);
                CAMX_FREE(pNode);
                pNode = NULL;

                continue;
            }
            tStart   = phRequestHandle->tStart;
            tEnd     = phRequestHandle->tEnd;
            hFence   = phRequestHandle->hChiFence;
            tCurrent = pJob->timestamp;

            if (CamxResultSuccess == pJob->resultStatus)
            {
                if (tCurrent < tStart || tCurrent < tEnd)
                {
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Data unavailable, requeue, tStart %llu tEnd %llu tCurrent %llu",
                                    tStart, tEnd, tCurrent);

                    continue;
                }

                // Trigger the fence on this and free the async data memory
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "triggering fence %d for t1 %llu t2 %llu",
                                    hFence, tStart, tEnd);

                // Signal the fence on which the consumer is waiting.
                m_pChiContext->SignalChiFence(hFence, pJob->resultStatus);
            }
            // Incase of error clear up all the fences pending to be signalled-with error.
            else
            {
                // Trigger the fence on this and free the async data memory
                CAMX_LOG_WARN(CamxLogGroupNCS, "Invalid state %s, triggering fence %d for t1 %llu t2 %llu",
                                CamxResultStrings[pJob->resultStatus],
                                hFence, tStart, tEnd);

                m_pChiContext->SignalChiFence(hFence, pJob->resultStatus);
            }

            // delete the processed request node
            CAMX_FREE(phRequestHandle);
            phRequestHandle = NULL;

            pClientRequestQ->RemoveNode(pNode);
            CAMX_FREE(pNode);
            pNode = NULL;
        }

        m_sensorConnList[connIndex].pRequestMutex->Unlock();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetDataSync
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSSensorDataHandle NCSIntfQSEE::GetDataSync(
    UINT64 tStart,
    UINT64 tEnd,
    INT    connIndex)
{
    INT                currentPos            = 0;
    INT                currentLocation       = 0;
    INT                prevLocation          = 0;
    INT                endLocation           = 0;
    INT                startLocation         = 0;
    UINT               bufferSize            = 0;
    UINT               bufferStride          = 0;
    UINT8*             phSensorBufferHandle  = NULL;
    NCSSensorData*     phNCSDataHandle       = NULL;
    CamxResult         result                = CamxResultSuccess;
    INT                bufferQLength         = 0;
    UINT64             tCurrent              = 0;
    UINT64             tPrevious             = 0;
    UINT64             tDiff                 = 0;
    INT                startOffset           = 0;
    INT                endOffset             = 0;
    Mutex*             pBufferMutex          = NULL;
    BOOL               isSensorValid         = TRUE;

    CAMX_ASSERT((tStart <= tEnd) && (connIndex >= 0) && (connIndex < static_cast<INT>(NCSMaxSupportedConns)));

    pBufferMutex         = m_sensorConnList[connIndex].bufferHandles.pBufferQMutex;

    CAMX_ASSERT_MESSAGE(NULL != pBufferMutex, "Buffer mutex is NULL");

    pBufferMutex->Lock();

    // Intialize buffer parameters
    bufferSize           = static_cast<UINT>(m_sensorConnList[connIndex].bufferHandles.bufferSize);
    bufferStride         = m_sensorConnList[connIndex].bufferHandles.bufferStride;
    currentPos           = m_sensorConnList[connIndex].bufferHandles.currentPos;
    phSensorBufferHandle = static_cast<UINT8*>(m_sensorConnList[connIndex].bufferHandles.phBufferHandle);

    CAMX_ASSERT_MESSAGE((NULL != phSensorBufferHandle) && (0 != bufferSize) && (0 != bufferStride),
                        "Invalid state to fetch data !!");

    bufferQLength = bufferSize / bufferStride;

    // Fetch a accessor object from the pool
    phNCSDataHandle = static_cast<NCSSensorData*>(GetAccessorObject());

    if ((NULL != phNCSDataHandle) &&
        (QSEEConnRunning == m_sensorConnList[connIndex].connectionState))
    {
        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Current Queue size %d pNCSDataHandle %p",
                       m_sensorDataObjectList.NumNodes(), phNCSDataHandle);

        // currentPos is the location where the next data would be added.
        currentLocation = (currentPos + bufferQLength - 1) % bufferQLength;
        prevLocation    = (currentPos + bufferQLength - 2) % bufferQLength;

        switch (m_sensorConnList[connIndex].sensorType)
        {
            case NCSGyroType:
                tCurrent  = reinterpret_cast<NCSDataGyro*>(phSensorBufferHandle + currentLocation*bufferStride)->timestamp;
                tPrevious = reinterpret_cast<NCSDataGyro*>(phSensorBufferHandle + prevLocation*bufferStride)->timestamp;
                break;
            case NCSAccelerometerType:
                tCurrent  = reinterpret_cast<NCSDataAccel*>(phSensorBufferHandle + currentLocation*bufferStride)->timestamp;
                tPrevious = reinterpret_cast<NCSDataAccel*>(phSensorBufferHandle + prevLocation*bufferStride)->timestamp;
                break;
            case NCSGravityType:
                tCurrent  = reinterpret_cast<NCSDataGravity*>(phSensorBufferHandle + currentLocation*bufferStride)->timestamp;
                tPrevious = reinterpret_cast<NCSDataGravity*>(phSensorBufferHandle + prevLocation*bufferStride)->timestamp;
                break;
            default:
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unsupported type");
                isSensorValid = FALSE;
                break;
        }

        m_sensorConnList[connIndex].bufferHandles.tickPeriod = static_cast<INT>(tCurrent - tPrevious);

        // Clamp the end time to current time if end time > current time
        if (tEnd > tCurrent)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "tstart %llu tend %llu tcurrent %llu",
                           tStart, tEnd, tCurrent);
            tEnd = tCurrent;
        }

        // Validate if the timetamps are in the valid order to fetch data
        if ((tStart <= tEnd) && (TRUE == isSensorValid))
        {

            // Validate if the tickperiod is valid to avoid divide by zero
            if (0 < m_sensorConnList[connIndex].bufferHandles.tickPeriod)
            {

                // Compute start location index
                tDiff         = tCurrent - tStart;
                startOffset   = static_cast<UINT>(tDiff / m_sensorConnList[connIndex].bufferHandles.tickPeriod);
                // Adjust for rounding error
                startOffset   += 1;
                startLocation = (bufferQLength + (currentLocation - startOffset)) % bufferQLength;

                // Compute end location index
                tDiff         = tCurrent - tEnd;
                endOffset     = static_cast<UINT>(tDiff / m_sensorConnList[connIndex].bufferHandles.tickPeriod);
                endLocation   = (bufferQLength + (currentLocation - endOffset)) % bufferQLength;

                if (startOffset < bufferQLength)
                {

                    result = phNCSDataHandle->SetDataLimits(startLocation, endLocation,
                                                            reinterpret_cast<VOID*>
                                                            (m_sensorConnList[connIndex].bufferHandles.
                                                                phBufferHandle),
                                                            bufferQLength);

                    if (CamxResultSuccess == result)
                    {
                        phNCSDataHandle->SetBufferStride(
                            m_sensorConnList[connIndex].bufferHandles.bufferStride);
                        SetBufferLock(
                            m_sensorConnList[connIndex].sensorType, startLocation, endLocation);
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to set data limits");
                        result = PutAccessorObject(phNCSDataHandle);
                        if (CamxResultSuccess != result)
                        {
                            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to push back the accessor !!");
                        }
                        phNCSDataHandle = NULL;
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Client asking for too much data");
                    result = PutAccessorObject(phNCSDataHandle);
                    if (CamxResultSuccess != result)
                    {
                        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to push back the accessor !!");
                    }

                    phNCSDataHandle = NULL;
                }

            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "NULL tick period, unable to compute terminal points of data");
                result = PutAccessorObject(phNCSDataHandle);
                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to push back the accessor !!");
                }

                phNCSDataHandle = NULL;
            }
        }
        else
        {
            /// todo (CAMX-2393) Give back the best case data
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to fetch data, future timestamp, data not present yet");

            result = PutAccessorObject(phNCSDataHandle);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to push back the accessor !!");
            }

            phNCSDataHandle = NULL;
        }

    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable a fetch a free data handle or connection in invalid state %d !!",
                       m_sensorConnList[connIndex].connectionState);
        phNCSDataHandle = NULL;
    }

    if (NULL == phNCSDataHandle)
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to give back accessor");
    }

    pBufferMutex->Unlock();
    return phNCSDataHandle;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetLastNSamples
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSSensorDataHandle NCSIntfQSEE::GetLastNSamples(
    UINT  numOfSamples,
    INT   connIndex)
{
    INT                currentPos           = 0;
    INT                startLocation        = 0;
    INT                endLocation          = 0;
    UINT               bufferSize           = 0;
    UINT               bufferStride         = 0;
    UINT8*             phSensorBufferHandle = NULL;
    NCSSensorData*     phNCSDataHandle      = NULL;
    CamxResult         result               = CamxResultSuccess;
    INT                bufferQLength        = 0;
    Mutex*             pBufferMutex         = NULL;

    pBufferMutex = m_sensorConnList[connIndex].bufferHandles.pBufferQMutex;

    CAMX_ASSERT_MESSAGE(NULL != pBufferMutex, "Invalid pBufferMutex %p", pBufferMutex);

    pBufferMutex->Lock();

    phSensorBufferHandle = static_cast<UINT8*>(m_sensorConnList[connIndex].bufferHandles.phBufferHandle);
    bufferSize           = static_cast<UINT>(m_sensorConnList[connIndex].bufferHandles.bufferSize);
    bufferStride         = m_sensorConnList[connIndex].bufferHandles.bufferStride;
    currentPos           = m_sensorConnList[connIndex].bufferHandles.currentPos;

    CAMX_ASSERT_MESSAGE((NULL != phSensorBufferHandle) && (0 != bufferSize) && (0 != bufferStride),
                        "Unable to save data, invalid state !!");

    bufferQLength = bufferSize / bufferStride;

    phNCSDataHandle = static_cast<NCSSensorData*>(GetAccessorObject());

    if ((NULL != phNCSDataHandle) &&
        (QSEEConnRunning == m_sensorConnList[connIndex].connectionState))
    {

        // Check if there is enough data in the ring buffer and if state is valid
        if (( (TRUE == m_sensorConnList[connIndex].bufferHandles.isBufferFull) ||
            ((static_cast<INT>(numOfSamples) <= currentPos)) ))
        {
            // CurrentPos is the location where the next data would be added.
            // Start location is less than end location.
            if (0 != currentPos)
            {
                endLocation = currentPos - 1;
            }
            else
            {
                endLocation = bufferQLength - 1;
            }

            if ((endLocation - static_cast<INT>(numOfSamples) + 1) < 0)
            {
                startLocation = bufferQLength + (endLocation - numOfSamples + 1);
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "endLocation %d bufferQLength %d startLocation %d numOfSamples %d",
                    startLocation, bufferQLength, endLocation, numOfSamples)
            }
            else
            {
                startLocation = (endLocation - numOfSamples + 1);
            }

            result  = phNCSDataHandle->SetDataLimits(startLocation,
                                                        endLocation,
                                                        reinterpret_cast<VOID*>(
                                                            m_sensorConnList[connIndex].
                                                            bufferHandles.phBufferHandle),
                                                        bufferQLength);

            if (CamxResultSuccess == result)
            {
                phNCSDataHandle->SetBufferStride(m_sensorConnList[connIndex].bufferHandles.bufferStride);

                // todo (CAMX-2393) readlock/write lock instead of counter to lock the access.
                // Set the lock flag in case the current cursor position where data would be queued is in the acessed range
                if (TRUE == SetBufferLock(m_sensorConnList[connIndex].sensorType, startLocation, endLocation))
                {
                    phNCSDataHandle->SetHaveBufLocked();
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to set data limits");
                result = PutAccessorObject(phNCSDataHandle);
                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to push back the accessor !!");
                }
                phNCSDataHandle =  NULL;
            }

        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS,
                            "Client asking too much data, not there: connIdx %d isBuffFull %d  numSamples %d currPos %d !!",
                            connIndex,
                            m_sensorConnList[connIndex].bufferHandles.isBufferFull,
                            numOfSamples,
                            currentPos);

            result = PutAccessorObject(phNCSDataHandle);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to push back the accessor !!");
            }

            phNCSDataHandle = NULL;
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "No more free data objects or connection in invalid state %d",
                       m_sensorConnList[connIndex].connectionState);
        phNCSDataHandle = NULL;
    }

    if (NULL == phNCSDataHandle)
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to give back accessor");
    }

    pBufferMutex->Unlock();
    return phNCSDataHandle;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::Release
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::Release(
    NCSSensorHandle hSensor)
{
    UINT8*           pRingBuffer   = NULL;
    ssc_connection*  pSensorConn   = NULL;
    NCSSensorData*   pSensorData   = NULL;
    NCSSensor*       pSensorObject = NULL;
    NCSAsyncRequest* pRequest      = NULL;
    NCSSensor*       pSensorObj    = NULL;
    INT              connIndex     = 0;

    CAMX_ASSERT(NULL != hSensor);

    pSensorObj = static_cast<NCSSensor*>(hSensor);

    connIndex = pSensorObj->GetConnIndex();

    CAMX_ASSERT_MESSAGE((connIndex >= 0) && (connIndex < static_cast<INT>(NCSMaxSupportedConns)),
                        "connIndex %d", connIndex);

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "OnRelease: Number of mapped clients at connIndex %d mappedClients %d",
                   connIndex,
                   m_sensorConnList[connIndex].mappedClients);

    /// When this happens to be the last mapped client, destroy connection and its resources
    /// Clean up all the sensor connection related objects viz : connection, circular buffers
    /// mutexes.
    if (1 == m_sensorConnList[connIndex].mappedClients)
    {
        pSensorConn =
            static_cast<ssc_connection*>(m_sensorConnList[connIndex].phSensorConnHandle);

        // Destroy the connection to keep callbacks from coming
        if (NULL != pSensorConn)
        {
            CAMX_DELETE pSensorConn;
            m_sensorConnList[connIndex].phSensorConnHandle = NULL;
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Disabling streaming for conn index %d", connIndex);
            pSensorConn = NULL;

            m_sensorConnList[connIndex].connectionState = QSEEConnStopped;
        }

        // Destroy the memory items being used in callback: Ring buffer mutex
        if (NULL != m_sensorConnList[connIndex].bufferHandles.pBufferQMutex)
        {
            m_sensorConnList[connIndex].bufferHandles.pBufferQMutex->Destroy();
            m_sensorConnList[connIndex].bufferHandles.pBufferQMutex = NULL;
        }

        // Destroy the memory items being used in callback: Ring buffer
        pRingBuffer = reinterpret_cast<UINT8*>(m_sensorConnList[connIndex].bufferHandles.phBufferHandle);
        if (NULL != pRingBuffer)
        {
            CAMX_FREE(pRingBuffer);
            m_sensorConnList[connIndex].bufferHandles.phBufferHandle = NULL;
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Releasing ring buffer for conn index %d", connIndex);
        }

        // Debug file handle close
        if (NULL != m_sensorConnList[connIndex].bufferHandles.pFileBuffer)
        {
            OsUtils::FClose(m_sensorConnList[connIndex].bufferHandles.pFileBuffer);
        }

        // Cleanup the async request Q, if any pending requests and destroy the queue
        m_sensorConnList[connIndex].pRequestMutex->Lock();

        LDLLNode* pNode = m_sensorConnList[connIndex].asyncRequestQ.RemoveFromHead();

        while (NULL != pNode)
        {
            pRequest = static_cast<NCSAsyncRequest*>(pNode->pData);
            if (NULL != pRequest)
            {
                CAMX_FREE(pRequest);
                pRequest = NULL;
            }
            CAMX_FREE(pNode);

            pNode = m_sensorConnList[connIndex].asyncRequestQ.RemoveFromHead();
        }
        m_sensorConnList[connIndex].pRequestMutex->Unlock();

        // Destroy Request queue mutex
        if (NULL != m_sensorConnList[connIndex].pRequestMutex)
        {
            m_sensorConnList[connIndex].pRequestMutex->Destroy();
            m_sensorConnList[connIndex].pRequestMutex = NULL;
        }

        // Destroy Request queue cond var
        if (NULL != m_sensorConnList[connIndex].pRequestCondVar)
        {
            m_sensorConnList[connIndex].pRequestCondVar->Destroy();
            m_sensorConnList[connIndex].pRequestCondVar = NULL;
        }

        m_sensorConnList[connIndex].isValid = FALSE;

        m_numActiveConns--;
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupNCS, "%d clients still mapped, not deleting the connection and resources",
                      m_sensorConnList[connIndex].mappedClients);
    }

    m_sensorConnList[connIndex].mappedClients--;
    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "OnRelease: %d remaining clients mapped to connIndex %d",
                     m_sensorConnList[connIndex].mappedClients,
                     connIndex);

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::ReconfigSession
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::ReconfigSession(
    NCSSensorConfig* pConfig,
    INT              connIndex,
    BOOL             recreateSSCLink)
{
    NCSSensor*      pSensorObject = NULL;
    sensor_uid*     pSensorSUID   = NULL;
    ssc_connection* pSSCConn      = NULL;
    RingBuffer*     pBuffer       = NULL;
    CamxResult      result        = CamxResultEFailed;

    CAMX_ASSERT(NULL != pConfig);

    if (QSEEConnRunning == m_sensorConnList[connIndex].connectionState)
    {
        CAMX_ASSERT_MESSAGE((connIndex >= 0) && (connIndex < static_cast<INT>(NCSMaxSupportedConns)),
                            "connIndex %d", connIndex);

        pSensorSUID = &m_sensorConnList[connIndex].suid;
        pBuffer     = &m_sensorConnList[connIndex].bufferHandles;

        pBuffer->pBufferQMutex->Lock();

        // Trigger pending async requests
        QSEEJob qseeJob      = { 0 };
        qseeJob.connIndex    = connIndex;
        qseeJob.resultStatus = CamxResultEDisabled;
        qseeJob.timestamp    = 0;

        result = TriggerClientFence(&qseeJob);

        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Triggering Client fence failed");
        }
        else
        {
            // if there is need to recreate the link
            if (TRUE == recreateSSCLink)
            {
                pSSCConn = m_sensorConnList[connIndex].phSensorConnHandle;
                if (NULL != pSSCConn)
                {
                    CAMX_DELETE pSSCConn;

                    // Create anew an ssc connection
                    pSSCConn = CAMX_NEW ssc_connection(
                        [this](const uint8_t* pData, size_t size)
                    {
                        this->SensorCallback(pData, size);
                    });
                    if (NULL != pSSCConn)
                    {
                        m_sensorConnList[connIndex].phSensorConnHandle = pSSCConn;
                        result = CamxResultSuccess;
                    }
                    else
                    {
                        m_sensorConnList[connIndex].phSensorConnHandle = NULL;
                        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to create a sensor QSEE connection");
                        result = CamxResultENoMemory;
                    }
                }
            }

            if (CamxResultSuccess == result)
            {
                // Send disable request to stop the callbacks
                SendDisableRequest(connIndex);
                m_sensorConnList[connIndex].connectionState = QSEEConnStopped;

                // Reset state variables of the ring buffer
                pBuffer->curPosLocked = FALSE;
                pBuffer->currentPos   = 0;
                pBuffer->isBufferFull = FALSE;

                // Reset ring buffer data to '0'
                Utils::Memset(pBuffer->phBufferHandle, 0, pBuffer->bufferSize);

                // Send reconfig request
                SendConfigRequest(connIndex, pConfig);
                m_sensorConnList[connIndex].connectionState = QSEEConnRunning;

                result = CamxResultSuccess;
            }

        }

        pBuffer->pBufferQMutex->Unlock();

    }
    else
    {
        result = CamxResultEInvalidState;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetNumSensors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT NCSIntfQSEE::GetNumSensors()
{
    return m_numSensors;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetNumActiveClients
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT NCSIntfQSEE::GetNumActiveClients()
{
    return m_numActiveClients;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::Initialize(
    ChiContext* pChiContext)
{
    CamxResult     result      = CamxResultSuccess;
    NCSSensorData* pSensorData = NULL;
    LDLLNode*      pNode       = NULL;

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Initializing QSEE");

    m_pQSEEIntfMutex        = Mutex::Create("QSEEIntfMutex");
    m_pQSEELinkUpdateMutex  = Mutex::Create("QSEELinkUpdateMutex");
    m_pQSEELinkUpdateCond   = Condition::Create("QSEELinkUpdateCondition");
    m_pChiContext           = pChiContext;

    CAMX_ASSERT((NULL != m_pQSEEIntfMutex)       &&
                (NULL != m_pQSEELinkUpdateMutex) &&
                (NULL != m_pQSEELinkUpdateCond));


    for (UINT i = 0; i < QSEEAccessorPoolLen; i++)
    {
        pSensorData = CAMX_NEW NCSSensorData();
        if (NULL != pSensorData)
        {
            pNode = static_cast<LDLLNode*>(CAMX_CALLOC(sizeof(LDLLNode)));

            if (NULL != pNode)
            {
                pNode->pData = pSensorData;
                m_sensorDataObjectList.InsertToTail(pNode);
            }
            else
            {
                result = CamxResultENoMemory;
                CAMX_DELETE pSensorData;
                pSensorData = NULL;
                break;
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to create sensor objects");
            result = CamxResultENoMemory;
            break;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::GetSensorsList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT NCSIntfQSEE::GetSensorsList()
{
    return m_sensorList;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::LockInterface
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::LockInterface()
{
    CamxResult result = CamxResultSuccess;

    if (NULL != m_pQSEEIntfMutex)
    {
        m_pQSEEIntfMutex->Lock();
    }
    else
    {
        result = CamxResultEInvalidPointer;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::UnlockInterface
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::UnlockInterface()
{
    CamxResult result = CamxResultSuccess;

    if (NULL != m_pQSEEIntfMutex)
    {
        m_pQSEEIntfMutex->Unlock();
    }
    else
    {
        result = CamxResultEInvalidPointer;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::FillCaps
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::FillCaps(
    NCSSensorCaps* pCaps,
    NCSSensorType  sensorType)
{
    CamxResult result = CamxResultEUnsupported;

    CAMX_ASSERT_MESSAGE((NULL != pCaps), "Pointer to be filled is NULL !");

    // if the sensor is available then fill the caps structure
    if (0 != (m_sensorList & (1 << sensorType)))
    {
        *pCaps = m_sensorCaps[sensorType];
        result = CamxResultSuccess;
    }
    // If the sensor is not available due to failure during boot up, try to probe once again.
    else
    {
        result = ProbeSensor(sensorType);

        if (CamxResultSuccess == result)
        {
            // If probe success, request for its capabilities and fill the input pointer.
            result = RequestSensorCaps(sensorType);

            if (CamxResultSuccess == result)
            {
                if (TRUE == m_sensorCaps[sensorType].isValid)
                {
                    *pCaps = m_sensorCaps[sensorType];
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Re-probe sensor %d successful !!", sensorType);
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Tried fetching caps, but they are invalid !");
                    result = CamxResultEFailed;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to get request capabilities !");
                result = CamxResultEFailed;
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to re-probe sensor %d !", sensorType);
            result = CamxResultEFailed;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SetupSensorLinkOnly
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID NCSIntfQSEE::SetupSensorLinkOnly(
    sensor_uid*  pSUID,
    NCSIntfQSEE* pNCSObject,
    const string &datatype)
{
    CAMX_UNREFERENCED_PARAM(pNCSObject);

    m_pQSEELinkUpdateMutex->Lock();

    // Register a callback function
    ssc_connection* phSSCHandle  = NULL;
    NCSSensorType   sensorType   = NCSMaxType;
    CamxResult      result       = CamxResultSuccess;

    if (0 == datatype.compare("gyro"))
    {
        sensorType = NCSGyroType;
        m_sensorList |= (1 << sensorType);
        m_numSensors += 1;
        CAMX_LOG_INFO(CamxLogGroupNCS, "GYRO sensor available numSensors %d Enabled sensor bitmap 0x%x",
            m_numSensors,
            m_sensorList);
    }
    else if (0 == datatype.compare("accel"))
    {
        sensorType = NCSAccelerometerType;
        m_sensorList |= (1 << sensorType);
        m_numSensors += 1;
        CAMX_LOG_INFO(CamxLogGroupNCS, "ACCEL sensor available numSensors %d Enabled sensor bitmap 0x%x",
            m_numSensors,
            m_sensorList);
    }
    else if (0 == datatype.compare("gravity"))
    {
        sensorType = NCSGravityType;
        m_sensorList |= (1 << sensorType);
        m_numSensors += 1;
        CAMX_LOG_INFO(CamxLogGroupNCS, "Gravity sensor available numSensors %d Enabled sensor bitmap 0x%x",
            m_numSensors,
            m_sensorList);
     }
    else if (0 == datatype.compare("gyro_cal"))
    {
        sensorType = NCSGyroCalType;
        m_sensorList |= (1 << sensorType);
        m_numSensors += 1;
        CAMX_LOG_INFO(CamxLogGroupNCS, "GyroCal sensor available numSensors %d Enabled sensor bitmap 0x%x",
                      m_numSensors,
                      m_sensorList);
    }
    else if (0 == datatype.compare("mag_cal"))
    {
        sensorType = NCSMagCalType;
        m_sensorList |= (1 << sensorType);
        m_numSensors += 1;
        CAMX_LOG_INFO(CamxLogGroupNCS, "MagCal sensor available numSensors %d Enabled sensor bitmap 0x%x",
                      m_numSensors,
                      m_sensorList);
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupNCS, "Currently unsupported sensor type %s", datatype.c_str());
        result = CamxResultEUnsupported;
    }

    // Create a connection link to ssc to query the capabilities of the probed sensors
    if (CamxResultSuccess == result)
    {
        if (NULL == m_pProbeLink)
        {

            /// This is a lamda function needed to be passed on to the Sensors API.
            /// The 'this' param is passed on so that it is populated with the data.
            phSSCHandle = CAMX_NEW ssc_connection(
                [this](const uint8_t* pData, size_t size)
                {
                    this->SensorCallback(pData, size);
                });

            if (NULL != phSSCHandle)
            {
                m_pProbeLink = phSSCHandle;
            }
            else
            {
                m_pProbeLink = NULL;
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to create a sensor QSEE connection");
            }
        }


        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Probed sensor: %s sensorType %d via probelink %p suidH 0x%x",
                         datatype.c_str(),
                         sensorType,
                         m_pProbeLink,
                         pSUID->high);

        m_suids[sensorType] = *pSUID;

    }

    m_pQSEELinkUpdateCond->Signal();
    m_pQSEELinkUpdateMutex->Unlock();

    return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SensorCallback
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SensorCallback
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID NCSIntfQSEE::SensorCallback(
    const uint8_t* pData,
    size_t size)
{
    sns_client_event_msg pb_event_msg;
    uint32_t             msg_id;
    NCSSensorType       sensorType = NCSMaxType;

    // NOWHINE GR017: Code is from protobuf, which is not QC code
    pb_event_msg.ParseFromArray(pData, static_cast<int>(size));

    sensor_uid suid(pb_event_msg.suid().suid_low(), pb_event_msg.suid().suid_high());

    for (INT i = 0; i < pb_event_msg.events_size(); i++)
    {
        const sns_client_event_msg_sns_client_event &pb_event = pb_event_msg.events(i);
        msg_id = pb_event.msg_id();
        switch (msg_id)
        {
            case SNS_STD_MSGID_SNS_STD_ERROR_EVENT:
            {
                sns_std_error_event error;
                error.ParseFromString(pb_event.payload());
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Received error event %i", error.error());
                break;
            }
            case SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_PHYSICAL_CONFIG_EVENT:
            {
                sns_std_sensor_physical_config_event config;
                config.ParseFromString(pb_event.payload());
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Received config event with sample rate %f", config.sample_rate());
                break;
            }
            case SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_EVENT:
            {
                if (CamxResultSuccess != FillSensorData(suid, pb_event))
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to set fill the data into the queue");
                }
                break;
            }
            case SNS_STD_MSGID_SNS_STD_ATTR_EVENT:
            {
                sns_std_attr_event attr_event;
                attr_event.ParseFromString(pb_event.payload());

                // For attribute events the sensor type needs to be fetched from the suids list
                sensorType = static_cast<NCSSensorType>(FindSensorType(suid));

                for (INT j = 0; j < attr_event.attributes_size(); j++)
                {
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "attribute ID :%d", attr_event.attributes(j).attr_id());
                }
                if (CamxResultSuccess != FillSensorAttributes(&attr_event, sensorType))
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to fill sensor attributes");
                }
                break;
            }
            case SNS_CAL_MSGID_SNS_CAL_EVENT:
            {

                if (CamxResultSuccess != FillCalibData(suid, pb_event))
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to handle Calibration event");
                }
                break;
            }
            default:
            {
                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Received unknown message ID %i", pb_event.msg_id())
                break;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::FillSensorAttributes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::FillSensorAttributes(
    sns_std_attr_event* pAttrEvent,
    NCSSensorType sensorType)
{
    INT attrId;

    m_pQSEELinkUpdateMutex->Lock();

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Filling attributes");
    for (INT i = 0; i < pAttrEvent->attributes_size(); i++)
    {
        attrId = pAttrEvent->attributes(i).attr_id();
        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "attribute ID :%d", attrId);

        switch (attrId)
        {
            case SNS_STD_SENSOR_ATTRID_RATES:
            {
                const sns_std_attr_value &attrValue = pAttrEvent->attributes(i).value();
                for (UINT j = 0; j < Utils::MinUINT32(attrValue.values_size(), NCSMaxRates); j++)
                {
                    if (attrValue.values(j).has_flt())
                    {
                        m_sensorCaps[sensorType].rates[j] = attrValue.values(j).flt();
                        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Float values of rates is %f", attrValue.values(j).flt());
                        m_sensorCaps[sensorType].numRates++;
                    }
                }
                break;
            }
            case SNS_STD_SENSOR_ATTRID_RESOLUTIONS:
            {
                const sns_std_attr_value &attrValue = pAttrEvent->attributes(i).value();
                for (UINT j = 0; j < Utils::MinUINT32(attrValue.values_size(), NCSMaxRates); j++)
                {
                    if (attrValue.values(j).has_flt())
                    {
                        m_sensorCaps[sensorType].resolutions[j] = attrValue.values(j).flt();
                        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Float values of resolutions is %f", attrValue.values(j).flt());
                        m_sensorCaps[sensorType].numResolutions++;
                    }
                }
                break;
            }
            case SNS_STD_SENSOR_ATTRID_RANGES:
            {
                const sns_std_attr_value &attrValue = pAttrEvent->attributes(i).value();
                for (UINT j = 0; j < Utils::MinUINT32(attrValue.values_size(), NCSMaxRates); j++)
                {
                    if (attrValue.values(j).has_flt())
                    {
                        m_sensorCaps[sensorType].ranges[j].start =
                            attrValue.values(j).subtype().values(0).flt();
                        m_sensorCaps[sensorType].ranges[j].end   =
                            attrValue.values(j).subtype().values(1).flt();
                        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Float values of ranges is %f and %f",
                                         attrValue.values(j).subtype().values(0).flt(),
                                         attrValue.values(j).subtype().values(1).flt());
                        m_sensorCaps[sensorType].numRanges++;
                    }
                }
                break;
            }
            default:
            {
                break;
            }
        }
    }
    m_sensorCaps[sensorType].isValid = TRUE;

    m_pQSEELinkUpdateCond->Signal();
    m_pQSEELinkUpdateMutex->Unlock();

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::FillSensorData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::FillSensorData(
    const sensor_uid &suid,
    const sns_client_event_msg_sns_client_event &pb_event)
{
    NCSDataGyro*         pGyroData      = NULL;
    NCSDataAccel*        pAccelData     = NULL;
    NCSDataGravity*      pGravityData   = NULL;
    INT                  currentPos     = 0;
    UINT                 bufferSize     = 0;
    UINT                 bufferStride   = 0;
    NCSSensorType        sensorType     = NCSMaxType;
    Mutex*               pBufferMutex   = NULL;
    VOID*                phBufferHandle = NULL;
    UINT                 bufferSamples  = 0;
    CamxResult           result         = CamxResultSuccess;
    NCSCalib             calibData      = { 0 };
    sns_std_sensor_event event;
    NCSJob*              pJob           = NULL;
    QSEEJob*             pQSEEJob       = NULL;
    INT                  connIndex      = -1;

    event.ParseFromString(pb_event.payload());

    connIndex  = FindSensorIndex(suid);

    if ((connIndex < 0) || (connIndex >= static_cast<INT>(NCSMaxSupportedConns)))
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Invalid sensor index %d", connIndex);
        return CamxResultEInvalidArg;
    }

    sensorType = m_sensorConnList[connIndex].sensorType;

    SetLastSeenTime(connIndex);

    pBufferMutex = m_sensorConnList[connIndex].bufferHandles.pBufferQMutex;
    if (NULL != pBufferMutex)
    {
        pBufferMutex->Lock();

        phBufferHandle = m_sensorConnList[connIndex].bufferHandles.phBufferHandle;
        bufferSize     = static_cast<UINT>(m_sensorConnList[connIndex].bufferHandles.bufferSize);
        bufferStride   = m_sensorConnList[connIndex].bufferHandles.bufferStride;
        currentPos     = m_sensorConnList[connIndex].bufferHandles.currentPos;

        bufferSamples = (bufferSize / bufferStride);

        CAMX_ASSERT((NULL != phBufferHandle) && (0 != bufferSize) && (0 != bufferStride));

        // Check if buffer is about to be filled and set the flag, to keep invalid data from going to client at the beginning.
        if ((currentPos < static_cast<INT>(bufferSamples - 1)) &&
            (FALSE == m_sensorConnList[connIndex].bufferHandles.isBufferFull))
        {
            m_sensorConnList[connIndex].bufferHandles.isBufferFull = TRUE;
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "isBufferFull TRUE Buffer is now full");
        }

        if (m_sensorConnList[connIndex].bufferHandles.curPosLocked <= 0)
        {

            switch (sensorType)
            {
                case NCSGyroType:
                    pGyroData =
                        reinterpret_cast<NCSDataGyro*>(reinterpret_cast<CHAR *>(phBufferHandle) + bufferStride*currentPos);
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS,
                                     "GYRO: Current Pos %d base addr %p computed addr %p ring bufferSize in bytes %d",
                                     currentPos,
                                     phBufferHandle,
                                     pGyroData,
                                     bufferSize);
                    if (TRUE == m_sensorConnList[connIndex].bufferHandles.calibData.isValid)
                    {
                        calibData = m_sensorConnList[connIndex].bufferHandles.calibData;
                    }

                    pGyroData->x         = event.data(0) - calibData.bias[0];
                    pGyroData->y         = event.data(1) - calibData.bias[1];
                    pGyroData->z         = event.data(2) - calibData.bias[2];
                    pGyroData->timestamp = pb_event.timestamp();
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Received Gyro sample <%f, %f, %f> time %llu bias < %f %f %f>",
                                     pGyroData->x, pGyroData->y, pGyroData->z, pb_event.timestamp(),
                                     calibData.bias[0],
                                     calibData.bias[1],
                                     calibData.bias[2]);

                    if (NULL != m_sensorConnList[connIndex].bufferHandles.pFileBuffer)
                    {
                        OsUtils::FPrintF(m_sensorConnList[connIndex].bufferHandles.pFileBuffer,
                                         "x %f y %f z %f ts %llu\n",
                                         event.data(0), event.data(1), event.data(2), pb_event.timestamp());
                    }

                    // ring buffer
                    currentPos                                             = (currentPos + 1) % bufferSamples;
                    m_sensorConnList[connIndex].bufferHandles.currentPos = currentPos;
                    break;

                case NCSAccelerometerType:
                    pAccelData =
                        reinterpret_cast<NCSDataAccel *>(reinterpret_cast<CHAR *>(phBufferHandle) + bufferStride*currentPos);
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "ACCEL: Current Pos %d base addr %p computed addr %p bufferSize %d",
                                     currentPos, phBufferHandle, pAccelData, bufferSize);
                    pAccelData->x         = event.data(0);
                    pAccelData->y         = event.data(1);
                    pAccelData->z         = event.data(2);
                    pAccelData->timestamp = pb_event.timestamp();
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Received Accel sample <%f, %f, %f> time %llu",
                                     event.data(0), event.data(1), event.data(2), pb_event.timestamp());

                    // ring buffer
                    currentPos                                             = (currentPos + 1) % (bufferSize / bufferStride);
                    m_sensorConnList[connIndex].bufferHandles.currentPos = currentPos;
                    break;

                case NCSGravityType:
                    pGravityData =
                        reinterpret_cast<NCSDataGravity *>(reinterpret_cast<CHAR *>(phBufferHandle) + bufferStride*currentPos);
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "GRAVITY: Current Pos %d base addr %p computed addr %p bufferSize %d",
                        currentPos, phBufferHandle, pGravityData, bufferSize);
                    pGravityData->x = event.data(0);
                    pGravityData->y = event.data(1);
                    pGravityData->z = event.data(2);

                    pGravityData->timestamp = pb_event.timestamp();
                    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Received GRAVITY sample <%f, %f, %f> time %llu",
                        event.data(0), event.data(1), event.data(2), pb_event.timestamp());

                    currentPos = (currentPos + 1) % (bufferSize / bufferStride); // ring buffer
                    m_sensorConnList[connIndex].bufferHandles.currentPos = currentPos;
                    break;
                default:
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unsupported sensor event, tossing away the data");
                    break;
            }

            // Enqueue job to NCS service thread for async processing.
            pJob       = static_cast<NCSJob*>(CAMX_CALLOC(sizeof(NCSJob)));
            pQSEEJob   = static_cast<QSEEJob*>(CAMX_CALLOC(sizeof(QSEEJob)));
            if ((NULL != pJob) && (NULL != pQSEEJob))
            {
                pQSEEJob->resultStatus   = CamxResultSuccess;
                pQSEEJob->timestamp      = pb_event.timestamp();
                pQSEEJob->connIndex      = connIndex;
                pJob->jobType            = NCSRequestProcType;
                pJob->pPayload           = pQSEEJob;

                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "timestamp %llu connIndex %d trigstatus %d",
                               pQSEEJob->timestamp,
                               pQSEEJob->connIndex,
                               pQSEEJob->resultStatus);

                if (NULL != m_pServiceObject)
                {
                    result = m_pServiceObject->EnqueueJob(pJob);
                    if (CamxResultSuccess != result)
                    {
                        CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to Enqueue job %s", CamxResultStrings[result]);
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "NCS Service object is NULL, unable to enqueue Job");
                    result = CamxResultEInvalidPointer;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to allocate job memory !");
                result = CamxResultENoMemory;
            }

        }
        else
        {
            CAMX_LOG_WARN(CamxLogGroupNCS, "Current position is locked for access by clients, dropping this data");
            result = CamxResultEReadOnly;
        }
        pBufferMutex->Unlock();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::FillCalibData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::FillCalibData(
    const sensor_uid &suid,
    const sns_client_event_msg_sns_client_event &pb_event)
{
    sns_cal_event pb_cal_event;
    NCSSensorType sensorType    = NCSMaxType;
    Mutex*        pBufMutex     =  NULL;
    CamxResult    result        = CamxResultSuccess;

    sensorType = static_cast<NCSSensorType>(FindSensorType(suid));

    CAMX_ASSERT((sensorType >= 0) && (sensorType < NCSMaxType));

    for (INT connIndex = 0; connIndex < static_cast<INT>(NCSMaxSupportedConns); connIndex++)
    {

        if ((TRUE == isCalibSource(sensorType)) &&
            (m_sensorConnList[connIndex].calibSUID == suid))
        {
            // Lock the corresponding buffer mutex, to keep non-bias compensated data from going into Q.
            pBufMutex = m_sensorConnList[connIndex].bufferHandles.pBufferQMutex;

            CAMX_ASSERT_MESSAGE((NULL != pBufMutex), "Invalid buffer mutex pointer !!");

            pBufMutex->Lock();

            // Fill the calibration data for all sensors associated with this calibration sensor.
            for (UINT i = 0; i < NCSMaxSupportedConns; i++)
            {
                if (m_sensorConnList[i].calibSUID == suid)
                {
                    pb_cal_event.ParseFromString(pb_event.payload());

                    if (SNS_STD_SENSOR_SAMPLE_STATUS_UNRELIABLE != pb_cal_event.status())
                    {
                        m_sensorConnList[connIndex].bufferHandles.calibData.bias[0] = pb_cal_event.bias(0);
                        m_sensorConnList[connIndex].bufferHandles.calibData.bias[1] = pb_cal_event.bias(1);
                        m_sensorConnList[connIndex].bufferHandles.calibData.bias[2] = pb_cal_event.bias(2);
                        m_sensorConnList[connIndex].bufferHandles.calibData.isValid = TRUE;

                        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Calibration event bias (%f %f %f) status %d sensor index %d",
                                         pb_cal_event.bias(0),
                                         pb_cal_event.bias(1),
                                         pb_cal_event.bias(2),
                                         pb_cal_event.status(),
                                         FindSensorIndex(suid));
                    }
                    else
                    {
                        m_sensorConnList[connIndex].bufferHandles.calibData.isValid = FALSE;
                        CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Unreliable calibration data");
                    }
                }
            }

            pBufMutex->Unlock();
        }
        else
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Not a calibration sensor");
            result = CamxResultSuccess;
        }
    }


    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::SetLastSeenTime
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::SetLastSeenTime(
    INT connIndex)
{
    QSEESensorConn* pConn  = NULL;
    CamxResult      result = CamxResultSuccess;

    CAMX_ASSERT((connIndex >= 0) && (connIndex < static_cast<INT>(NCSMaxSupportedConns)));

    pConn = &m_sensorConnList[connIndex];

    pConn->bufferHandles.pBufferQMutex->Lock();

    OsUtils::GetTime(&pConn->lastSeenTime);

    pConn->bufferHandles.pBufferQMutex->Unlock();


    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::RegisterService
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSSensorHandle NCSIntfQSEE::RegisterService(
    NCSSensorType   sensorType,
    VOID*           pConfigParamsSent)
{
    UINT             sensorList;
    NCSSensor*       pNCSSensorObject  = NULL;
    NCSSensorConfig* pConfigParams     = NULL;
    UINT             freeSlot          = 0;
    CamxResult       result            = CamxResultEFailed;

    CAMX_ASSERT(NULL != pConfigParamsSent);

    LockInterface();

    pConfigParams    = static_cast<NCSSensorConfig*>(pConfigParamsSent);

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "RegisterService for a client");

    sensorList = GetSensorsList();

    // if the sensor is available, only then create an object
    if (0 != (sensorList & (1 << sensorType)))
    {
        // todo (CAMX-2393) check if the sensor supports this caps
        pNCSSensorObject = CAMX_NEW NCSSensor(this, pConfigParams);

        if (NULL != pNCSSensorObject)
        {

            // Find a free slot to save the NCS client handle given to the client.
            for (freeSlot = 0; freeSlot < MaxSupportedSensorClients; freeSlot++)
            {
                // Break at the first occurence of a NULL slot
                if (NULL == m_pSensorClients[freeSlot])
                {
                    break;
                }
            }

            if (MaxSupportedSensorClients > freeSlot)
            {

                result = CreateClientSession(pNCSSensorObject);
                if (CamxResultSuccess != result)
                {
                    CAMX_DELETE pNCSSensorObject;
                    pNCSSensorObject = NULL;
                    CAMX_LOG_ERROR(CamxLogGroupNCS,
                                   "Unable to register to the NCS service, Unable to create client session %s!",
                                   CamxResultStrings[result]);
                }
                else
                {
                    m_pSensorClients[freeSlot] = pNCSSensorObject;
                    m_numActiveClients         = m_numActiveClients + 1;

                    CAMX_LOG_INFO(CamxLogGroupNCS,
                                  "client registerd for sensor type %d pNCSSensorObject %p Number of clients registered %d",
                                  pConfigParams->sensorType,
                                  pNCSSensorObject,
                                  m_numActiveClients);
                }
            }
            else
            {
                CAMX_DELETE pNCSSensorObject;
                pNCSSensorObject = NULL;
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to register to the NCS service, max client count reached !");
            }
        }

    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Sensor not enabled, sensorList 0x%x", sensorList);
        pNCSSensorObject = NULL;
    }

    UnlockInterface();

    return pNCSSensorObject;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::UnregisterService
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::UnregisterService(
    NCSSensorHandle handle)
{
    NCSSensor* pSensorObject    = NULL;
    UINT       numActiveClients = 0;
    CamxResult result           = CamxResultENoSuch;

    LockInterface();

    CAMX_LOG_VERBOSE(CamxLogGroupNCS, "UnregisterService for sensor handle %p", handle);

    if (NULL != handle)
    {

        if (0 != m_numActiveClients)
        {
            numActiveClients = m_numActiveClients;
            BOOL isFound = FALSE;
            UINT i       = 0;

            for (i = 0; i < MaxSupportedSensorClients; i++)
            {
                pSensorObject = m_pSensorClients[i];

                // If the list contains the sensor they registered for then only remove it.
                if (pSensorObject == handle)
                {
                    isFound = TRUE;
                    break;
                }
            }
            if (TRUE == isFound)
            {
                result = Release(pSensorObject);
                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Error releasing session associated with sensor conn idx %d type %d",
                                    pSensorObject->GetConnIndex(),
                                    pSensorObject->GetSensorType());
                }
                CAMX_DELETE pSensorObject;
                m_pSensorClients[i] = NULL;
                pSensorObject                       = NULL;
                m_numActiveClients  = m_numActiveClients - 1;

                CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Successful unregister: remaining active clients %d",
                                    m_numActiveClients);
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Sensor handle %p not found", handle);
                result = CamxResultENoSuch;
            }
        }
        else
        {
            result = CamxResultENoMore;
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "NULL sensor handle");
        result = CamxResultEInvalidPointer;
    }

    UnlockInterface();

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::AllocSensorStreamBuf
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* NCSIntfQSEE::AllocSensorStreamBuf(
    NCSSensorConfig config,
    size_t*         pBufferSize,
    UINT*           pBufferStride)
{
    UINT  lBufferSize   = 0;
    UINT  lBUfferStride = 0;
    VOID* pBuffer       = NULL;

    switch(config.sensorType)
    {
        case NCSAccelerometerType:
            lBufferSize   = sizeof(NCSDataAccel) * static_cast<INT>(config.samplingRate) * NCSMaxBufferedTime;
            lBUfferStride = sizeof(NCSDataAccel);
            break;
        case NCSGyroType:
            lBufferSize   = sizeof(NCSDataGyro)  * static_cast<INT>(config.samplingRate) * NCSMaxBufferedTime;
            lBUfferStride = sizeof(NCSDataGyro);
            break;
        case NCSGravityType:
            lBufferSize   = sizeof(NCSDataGravity)  * static_cast<INT>(config.samplingRate) * NCSMaxBufferedTime;
            lBUfferStride = sizeof(NCSDataGravity);
            break;
        default:
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unhandled sensor type !!");
            break;
    }

    if (lBufferSize > 0)
    {
        *pBufferSize    = lBufferSize;
        *pBufferStride  = lBUfferStride;

        pBuffer = CAMX_CALLOC(lBufferSize);
        if (NULL == pBuffer)
        {
            CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to allocate sensor buffer");
        }
        else
        {
            CAMX_LOG_VERBOSE(CamxLogGroupNCS, "Allocated buffer for sensor of type %d handle %p size %d stride %d",
               config.sensorType, pBuffer, lBufferSize, lBUfferStride);
        }
    }
    else
    {
        *pBufferSize = 0;
        CAMX_LOG_ERROR(CamxLogGroupNCS, "Invalid computed buffer size !!");
    }
    return pBuffer;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::EnqueueAccessor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::EnqueueAccessor(
    NCSSensorDataHandle hAccesorObj)
{
    CamxResult result = CamxResultSuccess;

    result = PutAccessorObject(hAccesorObj);
    if (CamxResultSuccess != result)
    {
        CAMX_LOG_ERROR(CamxLogGroupNCS, "AccessorObject is NULL");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::ProcessJob
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NCSIntfQSEE::ProcessJob(
    VOID* pPayload)
{
    CamxResult result     = CamxResultEFailed;
    QSEEJob*   pJob       = NULL;

    pJob =  static_cast<QSEEJob*>(pPayload);

    if (NULL != pJob)
    {
        result    = TriggerClientFence(pJob);
        CAMX_FREE(pJob);
        pJob = NULL;
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupNCS, "NULL payload pPayload %p", pPayload);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCSIntfQSEE::UpdateIntfState
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NCSIntfState NCSIntfQSEE::UpdateIntfState(
    VOID * pPayload)
{
    CAMX_UNREFERENCED_PARAM(pPayload);
    NCSIntfState    intfState  = NCSIntfRunning;
    QSEESensorConn* pConn      = NULL;
    UINT32          timeLag    = 0;
    QSEEJob         qseeJob    = { 0 };
    BOOL            isTimedOut = FALSE;
    CamxResult      result     = CamxResultEInvalidState;
    INT             activConns = 0;

    for (INT connIndex = 0; connIndex < static_cast<INT>(NCSMaxSupportedConns); connIndex++)
    {
        pConn = &m_sensorConnList[connIndex];

        if ((TRUE == pConn->isValid) &&
            (QSEEConnRunning == pConn->connectionState))
        {
            activConns++;

            volatile UINT32 lastSeenTimeInMillis = OsUtils::CamxTimeToMillis(&pConn->lastSeenTime);
            CamxTime currentTime                 = { 0 };
            OsUtils::GetTime(&currentTime);

            timeLag    = OsUtils::CamxTimeToMillis(&currentTime) - lastSeenTimeInMillis;
            isTimedOut = (QSEECallbacksTimeout < timeLag);

            // If timeout has occured.
            if (TRUE == isTimedOut)
            {
                CAMX_LOG_ERROR(CamxLogGroupNCS, "Timeout of conn idx %d ms last seenTime %d ms currentTime %d timeLag %d ms",
                               connIndex,
                               OsUtils::CamxTimeToMillis(&pConn->lastSeenTime),
                               OsUtils::CamxTimeToMillis(&currentTime),
                               timeLag);

                pConn->connectionState = QSEEConnTimedout;

                NCSSensorConfig sensorConfig = { 0 };
                sensorConfig.samplingRate    = m_sensorConnList[connIndex].curSamplingRate;
                sensorConfig.reportRate      = m_sensorConnList[connIndex].curReportRate;

                result = ReconfigSession(&sensorConfig, connIndex, TRUE);
                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupNCS, "Unable to reconfig on timeout %s",
                                   CamxResultStrings[result]);
                }
            }

        }
    }

    if (activConns > 0)
    {
        intfState = NCSIntfRunning;
    }
    else
    {
        intfState = NCSIntfTimeout;
    }

    return intfState;
}

CAMX_NAMESPACE_END
